﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using StockLib.Indicator;

namespace StockLib
{
    public partial class StockIndicator
    {
        public virtual int yAxisWidth { get; set; }
        public virtual int xAxisHeight { get; set; }
        public virtual float pointWidth { get; set; }

        public Rectangle rectAll;
        public Rectangle rectTitle;
        public Rectangle rectGraph;
        public Rectangle rectAxis;
        protected int thickness;

        protected int gridHeight = 30;
        private int minDays = 5;

        protected Padding graphMargin = new Padding(0, 10, 0, 10);
        protected float[] intervalRatio = new float[] { 0.2f, 0.5f };
        protected Font font;

        protected List<float> axisValueCache = new List<float>();
        protected List<float> axisPosCache = new List<float>();

        ~StockIndicator()
        {
        }

        public void DrawGraph(PaintEventArgs e, Rectangle rectAll)
        {
            DrawGraph(e, rectAll, true, true);
        }

        public virtual void DrawGraph(PaintEventArgs e, Rectangle rectAll, bool drawTitle, bool drawAxis)
        {
            Region clipOld = e.Graphics.Clip;
            e.Graphics.Clip = new Region(rectAll);

            CalcMinMax();

            PrepareDrawSetting(rectAll);

            if (drawTitle)
            {
                if (e.ClipRectangle.IntersectsWith(rectTitle))
                {
                    DrawGraphTitle(e.Graphics, rectTitle);
                    //System.Console.WriteLine("DrawGraphTitle");
                }
            }
            if (drawAxis)
            {
                if (e.ClipRectangle.IntersectsWith(rectGraph) || e.ClipRectangle.IntersectsWith(rectAxis))
                {
                    DrawGraphAxis(e.Graphics);
                    //System.Console.WriteLine("DrawGraphAxis");
                }
            }

            if (e.ClipRectangle.IntersectsWith(rectGraph))
            {
                DrawGraphData(e.Graphics);
                //System.Console.WriteLine("DrawGraphData");
            }

            e.Graphics.Clip = clipOld;
        }

        public virtual void DrawGraphTitle(Graphics g, Rectangle rect)
        {
        }

        public virtual void DrawGraphTitle(Graphics g, Rectangle rect, int index)
        {
        }

        public virtual void DrawGraphData(Graphics g)
        {
        }

        public virtual void DrawGraphAxis(Graphics g)
        {
            int yMajorTickWidth = 4;
            int yMinorTickWidth = 2;

            if (!(this is StockKIndicator))
            {
                g.DrawLine(StockProfile.Pens.AxisLinePen, rectAll.Left, rectAll.Top, rectAll.Right, rectAll.Top);
            }

            g.DrawLine(StockProfile.Pens.AxisLinePen, rectAxis.Left, rectAxis.Top, rectAxis.Left, rectAxis.Bottom);

            int gridNumber = rectGraph.Height / gridHeight;
            float yStart = valueMin;
            float yInterval = (valueMax - valueMin) / gridNumber;
            float yRatio = 1;

            float yMaxMin = valueMax - valueMin;
            while (yMaxMin >= 100000)
            {
                yRatio *= 10;
                yMaxMin /= 10;
            }

            float[] yIntervalAxisArray = new float[] { 1000, 100, 10, 1, 0.1f };
            foreach (float yi in yIntervalAxisArray)
            {
                if (yInterval > yi)
                {
                    float rMin = 1;
                    float yIntervalMin = Int32.MaxValue;
                    foreach (float r in intervalRatio)
                    {
                        float yIntervalTemp = (float)Math.Ceiling((yInterval / yRatio) * r / yi) * yi / r;
                        if (yIntervalTemp < yIntervalMin)
                        {
                            yIntervalMin = yIntervalTemp;
                            rMin = r;
                        }
                    }
                    yInterval = yIntervalMin;
                    yStart = (float)Math.Ceiling((yStart / yRatio) * rMin / yi) * yi / rMin;
                    break;
                }
            }
            yInterval *= yRatio;
            yStart *= yRatio;

            string format = "{0:##0.00}";
            if (yInterval > 1000)
                format = "{0:####0}";
            else if (yInterval > 100)
                format = "{0:##0.0}";
            else if (yInterval > 0.1)
                format = "{0:##0.00}";

            using (Pen pen = new Pen(StockProfile.Colors.AxisLineColor))
            {
                pen.Width = 1;
                pen.DashStyle = DashStyle.Custom;
                pen.DashPattern = new float[] { 1, 3 };

                Rectangle rectAxisLabel = new Rectangle();
                int yHeight = GetYHeight(yInterval);
                for (float yValue = yStart; yValue < valueMax; yValue += yInterval)
                {
                    int yPos = GetYPos(yValue);
                    g.DrawLine(pen, rectGraph.Left, yPos, rectGraph.Right, yPos);

                    g.DrawLine(Pens.Red, rectAxis.Left, yPos, rectAxis.Left + yMajorTickWidth, yPos);
                    if (yValue > 0)
                    {
                        string txtMajorAxis = string.Format(format, yValue / yRatio);
                        rectAxisLabel.Width = rectAxis.Width;
                        rectAxisLabel.Height = TextRenderer.MeasureText(txtMajorAxis, font).Height;
                        rectAxisLabel.X = rectAxis.Left;
                        rectAxisLabel.Y = yPos - rectAxisLabel.Height / 2;
                        TextRenderer.DrawText(g, txtMajorAxis, font, rectAxisLabel, StockProfile.Colors.AxisTextColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Right);
                    }

                    float yValueMinor = yValue - yInterval / 2;
                    int yPosMinor = GetYPos(yValueMinor);
                    if (yValueMinor > valueMin)
                    {
                        g.DrawLine(StockProfile.Pens.AxisLinePen, rectAxis.Left, yPosMinor, rectAxis.Left + yMinorTickWidth, yPosMinor);
                    }
                    if (yValueMinor > valueMin && yHeight > rectAxisLabel.Height * 5)
                    {
                        string txtMajorAxis = string.Format(format, yValueMinor / yRatio);
                        rectAxisLabel.Width = rectAxis.Width;
                        rectAxisLabel.Height = TextRenderer.MeasureText(txtMajorAxis, font).Height;
                        rectAxisLabel.X = rectAxis.Left;
                        rectAxisLabel.Y = yPosMinor - rectAxisLabel.Height / 2;
                        TextRenderer.DrawText(g, txtMajorAxis, font, rectAxisLabel, StockProfile.Colors.AxisTextColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Right);
                    }
                }

                //if (yRatio > 1)
                {
                    string txtRatio = string.Format("X{0}", yRatio);
                    Rectangle rectRatio = rectAxis;
                    rectRatio.Size = TextRenderer.MeasureText(txtRatio, font);
                    rectRatio.Height = (int)(rectRatio.Height * 1.2);
                    rectRatio.Width = (int)(rectRatio.Width * 1);
                    rectRatio.Y = rectAxis.Top;
                    rectRatio.X = rectAxis.Right - rectRatio.Width;
                    TextRenderer.DrawText(g, txtRatio, font, rectRatio, StockProfile.Colors.AxisTextColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Right);
                    g.DrawRectangle(StockProfile.Pens.AxisLinePen, rectRatio);
                }
            }
        }

        protected virtual void PrepareDrawSetting(Rectangle rectAll)
        {
            this.font = StockProfile.Fonts.GraphFont;

            this.rectAll = rectAll;

            this.rectTitle = rectAll;
            this.rectTitle.Height = TextRenderer.MeasureText("日线MA0123456789", font).Height * 2;
            this.rectTitle.Width = rectAll.Right - yAxisWidth;

            this.rectAxis = rectAll;
            this.rectAxis.Width = yAxisWidth;
            this.rectAxis.X = rectAll.Right - rectAxis.Width;

            this.rectGraph = rectAll;
            this.rectGraph.Y = rectTitle.Bottom;
            this.rectGraph.Width = rectAll.Width - rectAxis.Width;
            this.rectGraph.Height = rectAll.Height - rectTitle.Height;

            this.thickness = PopulateKWidth();
        }

        public void DuplicateDrawSetting(StockIndicator indicator)
        {
            this.font = indicator.font;
            this.rectAll = indicator.rectAll;
            this.rectTitle = indicator.rectTitle;
            this.rectAxis = indicator.rectAxis;
            this.rectGraph = indicator.rectGraph;
            this.thickness = indicator.thickness;
            this.gridHeight = indicator.gridHeight;
            this.graphMargin = indicator.graphMargin;
            this.valueMin = indicator.valueMin;
            this.valueMax = indicator.valueMax;
        }

        protected int GetXPos(int index)
        {
            float xPos = pointWidth * (index + 1 - indexStart) + rectAll.Left;
            return (int)xPos;
        }

        public int GetXIndex(int xPos)
        {
            float index = (xPos - rectAll.Left) / pointWidth + 0.5f + indexStart - 1;
            return (int)index;
        }

        protected int GetYPos(float yValue)
        {
            float yRatio = 1.0f * (rectGraph.Height - graphMargin.Top - graphMargin.Bottom) / (valueMax - valueMin);
            float yPos = rectGraph.Bottom - yRatio * (yValue - valueMin) - graphMargin.Bottom;
            return (int)yPos;
        }

        public float GetYValue(float yPos)
        {
            float yRatio = 1.0f * (rectGraph.Height - graphMargin.Top - graphMargin.Bottom) / (valueMax - valueMin);
            return (rectGraph.Bottom - yPos - graphMargin.Bottom) / yRatio + valueMin;
        }

        protected int GetYHeight(float ySpan)
        {
            float yRatio = 1.0f * (rectGraph.Height - graphMargin.Top - graphMargin.Bottom) / (valueMax - valueMin);
            float yHeight = yRatio * Math.Abs(ySpan);
            return (int)yHeight;
        }

        private int PopulateKWidth()
        {
            int showDays = (indexEnd - indexStart + 2);
            if (showDays < minDays + 2)
                showDays = minDays + 2;

            float xRatio = (rectAll.Width - graphMargin.Left - graphMargin.Right) / showDays;

            if (xRatio <= 3)
                return 0;

            if (xRatio <= 15)
                return (int)(xRatio * 0.3f);

            return (int)(xRatio * 0.35f);
        }

        protected string FormatParams(List<int> daysArray)
        {
            string result = string.Empty;
            foreach (int days in daysArray)
            {
                if (string.IsNullOrEmpty(result))
                {
                    result += days;
                }
                else
                {
                    result += "," + days;
                }
            }

            return result;
        }
    }
}
